package com.qianxx.viewlibrary.menu.slidemenu;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

public class SlideMenu extends ViewGroup {
	public static final int SCREEN_MENU = 0;
	public static final int SCREEN_MAIN = 1;
	private static final int SCREEN_INVALID = -1;

	private int mCurrentScreen;
	// private int mNextScreen = SCREEN_INVALID;

	private Scroller mScroller;
	// VelocityTracker用来追踪触摸事件的速率
	private VelocityTracker mVelocityTracker;
	private int mTouchSlop;

	private float mLastMotionX;
	private float mLastMotionY;

	private final static int TOUCH_STATE_REST = 0;
	private final static int TOUCH_STATE_SCROLLING = 1;
	private static final int SNAP_VELOCITY = 1000;

	public int mTouchState = TOUCH_STATE_REST;
	// mLocked用来判断当前的菜单是否关闭
	private boolean mLocked;
	private boolean mAllowLongPress;

	public SlideMenu(Context context) {
		this(context, null, 0);
	}

	public SlideMenu(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		mScroller = new Scroller(getContext());
		mCurrentScreen = SCREEN_MAIN;
		// getScaledTouchSlop是一个距离，表示滑动的时候，手的移动要大于这个距离才开始移动控件，如果小于这个距离就不触发移动控件
		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	}

	/**
	 * onMeasure用来丈量View本身以及子View的尺寸大小
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		measureViews(widthMeasureSpec, heightMeasureSpec);
	}

	public void measureViews(int widthMeasureSpec, int heightMeasureSpec) {
		View menuView = getChildAt(0);
		menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);

		View contentView = getChildAt(1);
		contentView.measure(widthMeasureSpec, heightMeasureSpec);
	}

	/**
	 * onLayout方法提供整个ViewGrop的可视范围以便子View的布局
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// 获得子View的个数
		int childCount = getChildCount();
		if (childCount != 2) {
			throw new IllegalStateException(
					"The childCount of SlidingMenu must be 2");
		}
		// 菜单
		View menuView = getChildAt(0);
		final int width = menuView.getMeasuredWidth();
		// 将菜单置于屏幕左侧，此时已超出屏幕范围，用户看不见
		menuView.layout(-width, 0, 0, menuView.getMeasuredHeight());
		// 标题
		View contentView = getChildAt(1);
		// 将标题置于屏幕顶部
		contentView.layout(0, 0, contentView.getMeasuredWidth(),
				contentView.getMeasuredHeight());
	}

	/**
	 * 当View中的所有子控件均被映射成xml后触发
	 */
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		View child;
		for (int i = 0; i < getChildCount(); i++) {
			child = getChildAt(i);
			child.setFocusable(true);
			child.setClickable(true);
		}
	}

	/**
	 * onInterceptTouchEvent是用来截获所有的窗口点击事件，根据事件的不同来对子控件或自身分发事件。如果该方法返回true,
	 * 那么子控件将获取不到任何点击事件 . ps:需要注意的是当ViewGroup在滚动触摸时都不要将事件分发给子控件，
	 * 只有在ViewGrop结束滚动，结束触摸并且菜单最终被打开时才将事件分发给子控件，以下算法都是遵循该思路。
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// 如何菜单是关闭的时候，不将事件分发给子控件，也就是说，当菜单关闭时，只有ViewGroup才能获得触屏事件
		if (mLocked) {
			return true;
		}

		// 在手指移动过程中，不将事件分发给子控件，也就是说，当手指触摸ViewGroup的时候子控件再也接收不到任何触摸事件
		final int action = ev.getAction();
		if ((action == MotionEvent.ACTION_MOVE)
				&& (mTouchState != TOUCH_STATE_REST)) {
			return true;
		}

		final float x = ev.getX();
		final float y = ev.getY();

		switch (action) {
			case MotionEvent.ACTION_DOWN:
				// Remember location of down touch
				mLastMotionX = x;
				mLastMotionY = y;
				mAllowLongPress = true;

				mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
						: TOUCH_STATE_SCROLLING;

				break;

			case MotionEvent.ACTION_MOVE:
				// 水平移动距离
				final int xDiff = (int) Math.abs(x - mLastMotionX);
				// 垂直移动距离
				final int yDiff = (int) Math.abs(y - mLastMotionY);

				final int touchSlop = mTouchSlop;
				boolean xMoved = xDiff > touchSlop;
				boolean yMoved = yDiff > touchSlop;

				if (xMoved || yMoved) {

					if (xMoved) {
						// Scroll if the user moved far enough along the X axis
						mTouchState = TOUCH_STATE_SCROLLING;
						// enableChildrenCache();
					}
					// Either way, cancel any pending longpress
					if (mAllowLongPress) {
						mAllowLongPress = false;
						// Try canceling the long press. It could also have been
						// scheduled
						// by a distant descendant, so use the mAllowLongPress flag
						// to block
						// everything
						final View currentScreen = getChildAt(mCurrentScreen);
						currentScreen.cancelLongPress();
					}
				}
				break;

			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:
				// Release the drag
				// clearChildrenCache();
				// 滚动结束，将事件分发给子控件
				mTouchState = TOUCH_STATE_REST;
				mAllowLongPress = false;
				break;
		}

		/*
		 * The only time we want to intercept motion events is if we are in the
		 * drag mode.
		 */
		return mTouchState != TOUCH_STATE_REST;
	}

	// /**
	// * View组件显示的内容可以通过cache机制保存为bitmap,首先要启用缓存
	// */
	// void enableChildrenCache() {
	// final int count = getChildCount();
	// for (int i = 0; i < count; i++) {
	// final View layout = (View) getChildAt(i);
	// layout.setDrawingCacheEnabled(true);
	// }
	// }
	//
	// /**
	// * 禁用缓存
	// */
	// void clearChildrenCache() {
	// final int count = getChildCount();
	// for (int i = 0; i < count; i++) {
	// final View layout = (View) getChildAt(i);
	// layout.setDrawingCacheEnabled(false);
	// }
	// }

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// if (mLocked) {
		// return true;
		// }

		if (mVelocityTracker == null) {
			// 初始化VelocityTracker,主要用来追踪触屏事件的速率
			mVelocityTracker = VelocityTracker.obtain();
		}
		// 将事件加入到VelocityTracker的实例中，这样VelocityTracker就可以来追踪触摸事件的速率
		mVelocityTracker.addMovement(ev);

		final int action = ev.getAction();
		final float x = ev.getX();

		switch (action) {
			case MotionEvent.ACTION_DOWN:
				// 当我们点击屏幕时，而之前Scroller还在滚动，则先停止滚动动画
				if (!mScroller.isFinished()) {
					mScroller.abortAnimation();
				}

				// Remember where the motion event started
				mLastMotionX = x;
				break;
			case MotionEvent.ACTION_MOVE:
				if (mTouchState == TOUCH_STATE_SCROLLING) {
					// Scroll to follow the motion event
					final int deltaX = (int) (mLastMotionX - x);
					mLastMotionX = x;
					// 左滑动
					if (deltaX < 0) {
						if (deltaX + getScrollX() >= -getChildAt(0).getWidth()) {
							scrollBy(deltaX, 0);
						}
						// 右滑动
					} else if (deltaX > 0) {
						final int availableToScroll = getChildAt(
								getChildCount() - 1).getRight()
								- getScrollX() - getWidth();

						if (availableToScroll > 0) {
							scrollBy(Math.min(availableToScroll, deltaX), 0);
						}
					}
				}
				break;
			case MotionEvent.ACTION_UP:
				if (mTouchState == TOUCH_STATE_SCROLLING) {
					final VelocityTracker velocityTracker = mVelocityTracker;
					// 计算当前的速度，表示1000毫秒钟运动了多少个像素
					velocityTracker.computeCurrentVelocity(1000);
					// 获得当前水平方向的速度
					int velocityX = (int) velocityTracker.getXVelocity();

					if (velocityX > SNAP_VELOCITY && mCurrentScreen == SCREEN_MAIN) {
						// Fling hard enough to move left
						// 打开菜单
						snapToScreen(SCREEN_MENU);
					} else if (velocityX < -SNAP_VELOCITY
							&& mCurrentScreen == SCREEN_MENU) {
						// Fling hard enough to move right
						// 关闭菜单
						snapToScreen(SCREEN_MAIN);
					} else {
						snapToDestination();
					}

					if (mVelocityTracker != null) {
						mVelocityTracker.recycle();
						mVelocityTracker = null;
					}
				}
				mTouchState = TOUCH_STATE_REST;
				break;
			case MotionEvent.ACTION_CANCEL:
				mTouchState = TOUCH_STATE_REST;
		}

		return true;
	}

	/**
	 * Scroller类是为了实现View平滑的一个Helper类，但设置Scroller的滚动位置时，并不会导致View的滚动，
	 * 通常用Scroller记录或计算View滚动的位置。此时重写computeScroll()来完成实际的滚动
	 */
	@Override
	public void computeScroll() {
		// 判断滚动是否完成
		if (mScroller.computeScrollOffset()) {
			// 完成实际滚动
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
		} // else if (mNextScreen != SCREEN_INVALID) {
		// mCurrentScreen = Math.max(0,
		// Math.min(mNextScreen, getChildCount() - 1));
		// mNextScreen = SCREEN_INVALID;
		// clearChildrenCache();
		// }
	}

	/**
	 * 在ViewGroup实际滚动时被执行
	 */
	@Override
	public void scrollTo(int x, int y) {
		super.scrollTo(x, y);
		// 边滚动边重绘
		postInvalidate();
	}

	// /**
	// * postInvalidate()被执行时，是由dispatchDraw用来绘制子View
	// */
	// @Override
	// protected void dispatchDraw(Canvas canvas) {
	// final int scrollX = getScrollX();
	// super.dispatchDraw(canvas);
	// canvas.translate(scrollX, 0);
	// }

	// /**
	// *
	// */
	// @Override
	// public boolean dispatchUnhandledMove(View focused, int direction) {
	// if (direction == View.FOCUS_LEFT) {
	// if (getCurrentScreen() > 0) {
	// snapToScreen(getCurrentScreen() - 1);
	// return true;
	// }
	// } else if (direction == View.FOCUS_RIGHT) {
	// if (getCurrentScreen() < getChildCount() - 1) {
	// snapToScreen(getCurrentScreen() + 1);
	// return true;
	// }
	// }
	// return super.dispatchUnhandledMove(focused, direction);
	// }

	protected void snapToScreen(int whichScreen) {

		// enableChildrenCache();

		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		boolean changingScreens = whichScreen != mCurrentScreen;

		// mNextScreen = whichScreen;

		View focusedChild = getFocusedChild();
		if (focusedChild != null && changingScreens
				&& focusedChild == getChildAt(mCurrentScreen)) {
			focusedChild.clearFocus();
		}

		final int newX = (whichScreen - 1) * getChildAt(0).getWidth();
		final int delta = newX - getScrollX();
		// 计算滑动距离，并未真正实现View的滑动
		mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
		invalidate();
	}

	protected void snapToDestination() {
		if (getScrollX() == 0) {
			return;
		}
		final int screenWidth = getChildAt(0).getWidth();
		final int whichScreen = (screenWidth + getScrollX() + (screenWidth / 2))
				/ screenWidth;
		snapToScreen(whichScreen);
	}

	// public int getCurrentScreen() {
	// return mCurrentScreen;
	// }
	//
	// public boolean isMainScreenShowing() {
	// return mCurrentScreen == SCREEN_MAIN;
	// }

	public void openMenu() {
		mCurrentScreen = SCREEN_MENU;
		snapToScreen(mCurrentScreen);
	}

	public void closeMenu() {
		mCurrentScreen = SCREEN_MAIN;
		snapToScreen(mCurrentScreen);
	}

	public void unlock() {
		mLocked = false;
	}

	public void lock() {
		mLocked = true;
	}

}
